package net.realsee.service.wcs.rcs.hik.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.realsee.common.constant.GclCcsConstants;
import net.realsee.common.constant.HttpStatus;
import net.realsee.common.enums.wcs.ccs.GclLocationTaskStatusEnum;
import net.realsee.common.enums.wcs.charact.WcsCharactEnum;
import net.realsee.common.enums.wcs.hik.HikCallbackMethodTypeEnum;
import net.realsee.common.enums.wcs.hik.HikRcsOperateErrorEnum;
import net.realsee.common.enums.wcs.intf.InterfaceCodeEnum;
import net.realsee.common.enums.wcs.intf.InterfaceResultCodeEnum;
import net.realsee.common.enums.wcs.mes.GclMesRunFlagEnum;
import net.realsee.common.enums.wcs.order.OrderProcessStatusEnum;
import net.realsee.common.enums.wcs.order.OrderStatusEnums;
import net.realsee.common.enums.wcs.order.OrderTypeEnums;
import net.realsee.common.exception.mes.GclMesTaskException;
import net.realsee.common.utils.InterfaceUtils;
import net.realsee.common.utils.PlcUtil;
import net.realsee.common.utils.Threads;
import net.realsee.common.utils.spring.SpringUtils;
import net.realsee.domain.wcs.device.bo.PlcReadWriteBo;
import net.realsee.domain.wcs.intf.bo.InterfaceRequest;
import net.realsee.domain.wcs.intf.bo.InterfaceResponse;
import net.realsee.domain.wcs.rcs.hik.HikRcsOperateResult;
import net.realsee.domain.wcs.rcs.hik.HikWarnReq;
import net.realsee.domain.wcs.rcs.hik.v4.*;
import net.realsee.domain.wcs.warehouse.WcsWarehouseLocation;
import net.realsee.domain.wcs.warehouse.bo.LocationFullInfoBo;
import net.realsee.domain.wcs.work.WcsOrderDetail;
import net.realsee.domain.wcs.work.WcsOrderHeader;
import net.realsee.gcl.domain.bo.ccs.GclCcsLocationPosFixResult;
import net.realsee.gcl.domain.bo.mes.GclMesOperateResult;
import net.realsee.gcl.domain.bo.mes.GclMesTaskStatusReportReq;
import net.realsee.gcl.domain.bo.wcs.ContainerPositionBo;
import net.realsee.gcl.service.IGclCcsService;
import net.realsee.gcl.service.IGclLocationExtendService;
import net.realsee.gcl.service.IGclMesService;
import net.realsee.service.system.ISysConfigService;
import net.realsee.service.wcs.device.IWcsPlcTagService;
import net.realsee.service.wcs.rcs.hik.IHikV4Service;
import net.realsee.service.wcs.warehouse.IWcsWarehouseLocationService;
import net.realsee.service.wcs.work.IWcsOrderDetailService;
import net.realsee.service.wcs.work.IWcsOrderHeaderService;
import net.realsee.service.wcs.work.IWcsWarnService;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * 海康RCS接口V4服务实现
 *
 * @author 孙正华
 * @since 2023/10/11 15:23
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class HikV4ServiceImpl implements IHikV4Service {

    private final IWcsOrderHeaderService orderHeaderService;
    private final IWcsOrderDetailService orderDetailService;
    private final IWcsWarehouseLocationService locationService;
    private final IGclCcsService gclCcsService;
    private final IWcsWarnService wcsWarnService;
    private final IWcsPlcTagService plcTagService;
    private final ScheduledExecutorService scheduledExecutorService;
    private final IGclLocationExtendService locationExtendService;
    private final ISysConfigService configService;

    @Override
    public HikRcsOperateResult taskSubmit(String taskCode, String taskType,
                                          String startMapMark, String waitMapMark, String endMapMark, Integer priority) {
        List<HikTaskTargetRoute> routeList = new ArrayList<>();
        routeList.add(HikTaskTargetRoute.builder().type("SITE").autoStart(1).seq(0).code(startMapMark).build());
        if (StrUtil.isNotBlank(waitMapMark)) {
            // 有途径等待点，也需要在创建任务的时候传过去
            routeList.add(HikTaskTargetRoute.builder().type("SITE").autoStart(1).seq(0).code(waitMapMark).build());
        }
        routeList.add(HikTaskTargetRoute.builder().type("SITE").autoStart(1).seq(0).code(endMapMark).build());

        // 提交参数
        HikTaskSubmitReq submitReq = HikTaskSubmitReq.builder()
            .robotTaskCode(taskCode)
            .taskType(taskType)
            .targetRoute(routeList)
            .initPriority(priority)
            .extra(HikTaskExtra.builder().async("0").build())
            .build();
        return this.execute(taskCode, JSONUtil.toJsonStr(submitReq), InterfaceCodeEnum.HIK_RCS_V4_TASK_SUBMIT);
    }

    @Override
    public HikRcsOperateResult taskContinue(String taskCode, String endMapMark) {
        // 提交参数
        HikTaskContinueReq taskContinueReq = HikTaskContinueReq.builder()
            .triggerType("TASK")
            .triggerCode(taskCode)
            .robotTaskCode(taskCode)
            .build();
        // 判断是否还有下个点位
        if (StrUtil.isNotBlank(endMapMark)) {
            // 构建下个点位
            taskContinueReq.setTargetRoute(HikTaskTargetRoute.builder().type("SITE").autoStart(1).seq(1).code(endMapMark).operation("DELIVERY").build());
        }
        return this.execute(taskCode, JSONUtil.toJsonStr(taskContinueReq), InterfaceCodeEnum.HIK_RCS_V4_TASK_CONTINUE);
    }

    @Override
    public HikRcsOperateResult taskCancel(String taskCode) {
        // 提交参数
        HikTaskCancelReq hikTaskCancelReq = HikTaskCancelReq.builder()
            .cancelType("DROP")
            .robotTaskCode(taskCode)
            .build();
        return this.execute(taskCode, JSONUtil.toJsonStr(hikTaskCancelReq), InterfaceCodeEnum.HIK_RCS_V4_TASK_CANCEL);
    }

    @Override
    public HikRcsOperateResult setTaskPriority(String taskCode, Integer priority) {
        // 提交参数
        HikTaskPriorityReq hikTaskPriorityReq = HikTaskPriorityReq.builder()
            .robotTaskCode(taskCode)
            .initPriority(priority)
            .build();
        return this.execute(taskCode, JSONUtil.toJsonStr(hikTaskPriorityReq), InterfaceCodeEnum.HIK_RCS_V4_TASK_PRIORITY);
    }

    @Override
    public HikRcsOperateResult adjustPosition(String taskCode, String eqptCode, Integer xOffset, Integer yOffset, Integer angle) {
        HikAgvAdjustPositionReq.AdjustPositionValue values = new HikAgvAdjustPositionReq.AdjustPositionValue();
        values.setXoffset(ObjectUtil.isNotNull(xOffset) ? xOffset.toString() : "0");
        values.setYoffset(ObjectUtil.isNotNull(yOffset) ? yOffset.toString() : "0");
        values.setPodDir(ObjectUtil.isNotNull(angle) ? angle.toString() : "0");
        HikAgvAdjustPositionReq.AdjustPositionExtra extra = new HikAgvAdjustPositionReq.AdjustPositionExtra();
        extra.setValues(values);

        // 提交参数
        HikAgvAdjustPositionReq adjustPositionReq = HikAgvAdjustPositionReq.builder()
            .taskCode(taskCode)
            .eqptCode(eqptCode)
            .actionStatus("6")
            .extra(extra)
            .build();
        return this.execute(taskCode, JSONUtil.toJsonStr(adjustPositionReq), InterfaceCodeEnum.HIK_RCS_V4_ADJUST_POSITION);
    }

    private HikRcsOperateResult execute(String taskCode, String body, InterfaceCodeEnum interfaceCodeEnum) {
        HikRcsOperateResult result = null;
        HttpResponse httpResponse = InterfaceUtils.callByHttp(
            InterfaceRequest.builder()
                .interfaceCode(interfaceCodeEnum)
                .taskId(taskCode)
                .keyword1(taskCode)
                .bodyStr(body)
                .headerObj(new HashMap<String, String>() {{
                    put("X-lr-request-id", IdUtil.getSnowflakeNextIdStr());
                }})
                .build());
        if (ObjectUtil.isNotNull(httpResponse) && httpResponse.isOk()) {
            result = JSONUtil.toBean(httpResponse.body(), HikRcsOperateResult.class);
        }
        return result;
    }

    /**
     * 任务执行回调
     */
    @Override
    public HikRcsOperateResult callback(HikRcsV4AgvCallback param) {
        // 海康RCS回调结果
        HikRcsOperateResult result = HikRcsOperateResult.builder()
            .code(HikRcsOperateErrorEnum.SUCCESS.getCode())
            .reqCode(param.getCurrentTaskCode())
            .build();
        // 入站日志待更新对象
        InterfaceResponse inLogForUpdate = InterfaceResponse.builder()
            .taskId(param.getCurrentTaskCode())
            .keyword1(param.getCurrentTaskCode())
            .keyword2(param.getCallbackMethod())
            .content(JSONUtil.toJsonStr(result))
            .httpCode((long) HttpStatus.SUCCESS)
            .status(InterfaceResultCodeEnum.SUCCESS.getCode())
            .build();
        try {
            this.handleHikRcsCallback(param);
        } catch (Exception e) {
            // 异常时，设置错误信息
            result.setCode(HikRcsOperateErrorEnum.UNKNOWN_ERROR.getCode()).setData(e.getMessage());
            inLogForUpdate.setHttpCode((long) HttpStatus.ERROR).setStatus(InterfaceResultCodeEnum.FAILED.getCode()).setContent(JSONUtil.toJsonStr(result));
        } finally {
            // 更新入站日志内容
            InterfaceUtils.updateInlogByResponse(inLogForUpdate);
        }
        return result;
    }

    /**
     * 任务回调处理
     */
    private void handleHikRcsCallback(HikRcsV4AgvCallback param) {
        // 校验是否是WCS创建的任务，如果不是则直接忽略
        if (this.createByWcs(param)) {
            // 1.更新1020的单据头
            this.updateOrderHeader(param);
            // 2.新增1020的单据明细
            this.createOrderDetail(param);
            // 3.更新库位状态
            this.updateWarehouseLocationStatus(param);
            // 4.任务状态回传MES
            this.reportTaskStatusToMes(param);
            // 5.处理小车偏差逻辑
            this.handleContainerPosition(param);
            // 6.处理立库二段任务终点库位设置逻辑
            this.handleLKSecondTaskFinalLocSet(param);
        }
    }

    private Boolean createByWcs(HikRcsV4AgvCallback param) {
        WcsOrderHeader orderHeader = orderHeaderService.getByOrderNoAndOrderType(param.getCurrentTaskCode(), OrderTypeEnums.UP_DOWN.getCode());
        return ObjectUtil.isNotNull(orderHeader);
    }

    /**
     * 更新单据头
     */
    private void updateOrderHeader(HikRcsV4AgvCallback param) {
        WcsOrderHeader orderHeader = orderHeaderService.getByOrderNoAndOrderType(param.getCurrentTaskCode(), OrderTypeEnums.UP_DOWN.getCode());
        orderHeader.setVehicleNo(param.getSingleRobotCode());
        String method = param.getCallbackMethod();
        // ProgressStatus 作为MES那边的任务是否开始标识了
        if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.START.getCode())) {
            // 任务开始
            orderHeader.setOrderStatus(OrderStatusEnums.STARTED.getCode());
            orderHeader.setProgressStatus(OrderProcessStatusEnum._1010010.getCode());
            orderHeader.setStartTime(DateUtil.date());
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.POD_SCAN.getCode())) {
            // 举升后校验
            orderHeader.setOrderStatus(OrderStatusEnums.STARTED.getCode());
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.OUT_BIN.getCode())) {
            // 举升后走出储位
            orderHeader.setOrderStatus(OrderStatusEnums.STARTED.getCode());
            orderHeader.setProgressStatus(OrderProcessStatusEnum._1010020.getCode());
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.WAITING.getCode())) {
            // 入库前等待校验（后续执行，需要定时任务拉起）
            orderHeader.setOrderStatus(OrderStatusEnums.HANG_UP.getCode());
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.APPLY_FROM_AGV.getCode())) {
            // 处理小车偏差逻辑
            orderHeader.setOrderStatus(OrderStatusEnums.STARTED.getCode());
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.END.getCode())) {
            // 任务全部完成
            orderHeader.setOrderStatus(OrderStatusEnums.COMPLETE.getCode());
            orderHeader.setCompletionTime(DateUtil.date());
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.CANCEL.getCode())) {
            // 任务取消
            orderHeader.setOrderStatus(OrderStatusEnums.CANCEL.getCode());
            orderHeader.setCompletionTime(DateUtil.date());
        }
        orderHeaderService.updateById(orderHeader);
    }

    /**
     * 新增单据明细
     */
    private void createOrderDetail(HikRcsV4AgvCallback param) {
        WcsOrderHeader orderHeader = orderHeaderService.getByOrderNoAndOrderType(param.getCurrentTaskCode(), OrderTypeEnums.UP_DOWN.getCode());
        WcsOrderDetail orderDetail = BeanUtil.copyProperties(param, WcsOrderDetail.class);
        orderDetail.setOrderNo(param.getCurrentTaskCode());
        orderDetail.setOrderType(OrderTypeEnums.UP_DOWN.getCode());
        orderDetail.setInlineInputs(JSONUtil.toJsonStr(param));
        orderDetail.setTaskType(orderHeader.getTaskType());
        orderDetail.setStartTime(orderHeader.getStartTime());
        orderDetail.setCompletionTime(DateUtil.date());
        String method = param.getCallbackMethod();
        orderDetail.setMethod(method);
        if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.START.getCode())) {
            // 任务开始
            orderDetail.setProgressStatus(OrderProcessStatusEnum._1010020.getCode());
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.POD_SCAN.getCode())) {
            // 举升后校验
            orderDetail.setProgressStatus(OrderProcessStatusEnum._1010030.getCode());
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.OUT_BIN.getCode())) {
            // 走出储位
            orderDetail.setProgressStatus(OrderProcessStatusEnum._1010050.getCode());
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.WAITING.getCode())) {
            // 入库前等待校验（后续执行，需要定时任务拉起）
            orderDetail.setProgressStatus(OrderProcessStatusEnum._1010070.getCode());
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.APPLY_FROM_AGV.getCode())) {
            // 处理小车偏差逻辑
            orderDetail.setProgressStatus(OrderProcessStatusEnum._1010080.getCode());
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.END.getCode())) {
            // 任务全部完成
            orderDetail.setProgressStatus(OrderProcessStatusEnum._1010110.getCode());
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.CANCEL.getCode())) {
            // 任务取消
            orderDetail.setProgressStatus(OrderProcessStatusEnum._1010100.getCode());
        }
        orderDetailService.save(orderDetail);
    }

    /**
     * 更新库位状态
     */
    private void updateWarehouseLocationStatus(HikRcsV4AgvCallback param) {
        String currentPositionCode = param.getCurrentPositionCode();
        String orderNo = param.getCurrentTaskCode();
        String method = param.getCallbackMethod();
        WcsWarehouseLocation warehouseLocation;
        if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.WAITING.getCode())) {
            // 在等待位回调的时候，返回的是等待位的地图点，需要查询终点
            warehouseLocation = locationService.getByWaitMapMark(currentPositionCode);
        } else {
            warehouseLocation = locationService.getByMapMark(currentPositionCode);
        }
        if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.OUT_BIN.getCode())) {
            locationExtendService.setWcsLocationTaskStatus(warehouseLocation.getLocation(), GclLocationTaskStatusEnum.OUT_BIN.getCode());
            // 货架从该库位已搬出
            warehouseLocation.setTaskCode(orderNo);
            warehouseLocation.setTaskRcsStatus(method);
            warehouseLocation.setTaskUpdateOn(DateUtil.date());
            locationService.updateById(warehouseLocation);
            // PLC 离站通知
            String wcsRequestInTag = locationService.getValueByLocationAndCharact(warehouseLocation.getLocation(), WcsCharactEnum.WCS_HAS_OUT);
            if (StrUtil.isNotBlank(wcsRequestInTag)) {
                PlcReadWriteBo plcReadWriteBo = plcTagService.getByTagCode(wcsRequestInTag);
                if (ObjectUtil.isNotNull(plcReadWriteBo)) {
                    plcReadWriteBo.setToWriteValue("true");
                    try {
                        // 异步5秒后执行
                        scheduledExecutorService.schedule(() -> {
                            PlcUtil.write(plcReadWriteBo);
                        }, 5, TimeUnit.SECONDS);
                    } catch (Exception e) {
                        log.error("脱胶下工装，写离站通知标识失败。");
                    }
                }
            }
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.END.getCode(), HikCallbackMethodTypeEnum.FIRST_HALF_END.getCode())) {
            locationExtendService.setWcsLocationTaskStatus(warehouseLocation.getLocation(), GclLocationTaskStatusEnum.IN_BIN.getCode());
            // 货架已搬入该库位
            warehouseLocation.setTaskCode(orderNo);
            warehouseLocation.setTaskRcsStatus(HikCallbackMethodTypeEnum.IN_BIN.getCode());
            warehouseLocation.setTaskUpdateOn(DateUtil.date());
            locationService.updateById(warehouseLocation);
            // PLC 到站通知
            String wcsRequestInTag = locationService.getValueByLocationAndCharact(warehouseLocation.getLocation(), WcsCharactEnum.WCS_HAS_IN);
            if (StrUtil.isNotBlank(wcsRequestInTag)) {
                PlcReadWriteBo plcReadWriteBo = plcTagService.getByTagCode(wcsRequestInTag);
                if (ObjectUtil.isNotNull(plcReadWriteBo)) {
                    plcReadWriteBo.setToWriteValue("true");
                    try {
                        PlcUtil.write(plcReadWriteBo);
                        // 更新请求进站为false
                        wcsRequestInTag = locationService.getValueByLocationAndCharact(warehouseLocation.getLocation(), WcsCharactEnum.WCS_REQUEST_IN);
                        if (StrUtil.isNotBlank(wcsRequestInTag)) {
                            plcReadWriteBo = plcTagService.getByTagCode(wcsRequestInTag);
                            if (ObjectUtil.isNotNull(plcReadWriteBo)) {
                                plcReadWriteBo.setToWriteValue("false");
                                try {
                                    PlcUtil.write(plcReadWriteBo);
                                } catch (Exception e) {
                                    log.error("脱胶下工装，写请求进站标识失败。");
                                }
                            }
                        }
                        // PLC 离站申请
                        wcsRequestInTag = locationService.getValueByLocationAndCharact(warehouseLocation.getLocation(), WcsCharactEnum.WCS_REQUEST_OUT);
                        if (StrUtil.isNotBlank(wcsRequestInTag)) {
                            plcReadWriteBo = plcTagService.getByTagCode(wcsRequestInTag);
                            if (ObjectUtil.isNotNull(plcReadWriteBo)) {
                                plcReadWriteBo.setToWriteValue("true");
                                try {
                                    PlcUtil.write(plcReadWriteBo);
                                } catch (Exception e) {
                                    log.error("脱胶下工装，写离站请求标识失败。");
                                }
                            }
                        }
                    } catch (Exception e) {
                        log.error("脱胶下工装，写到站通知标识失败。");
                    }
                }
            }
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.WAITING.getCode())) {
            locationExtendService.setWcsLocationTaskStatus(warehouseLocation.getLocation(), GclLocationTaskStatusEnum.WAITING.getCode());
            // AGV已进入等待位
            warehouseLocation.setTaskCode(orderNo);
            warehouseLocation.setTaskRcsStatus(HikCallbackMethodTypeEnum.WAITING.getCode());
            warehouseLocation.setTaskUpdateOn(DateUtil.date());
            locationService.updateById(warehouseLocation);
            // PLC 请求进站
            // 当任务类型为脱胶下工装，需要写入PLC，通知机械臂请求进入
            String wcsRequestInTag = locationService.getValueByLocationAndCharact(warehouseLocation.getLocation(), WcsCharactEnum.WCS_REQUEST_IN);
            if (StrUtil.isNotBlank(wcsRequestInTag)) {
                PlcReadWriteBo plcReadWriteBo = plcTagService.getByTagCode(wcsRequestInTag);
                if (ObjectUtil.isNotNull(plcReadWriteBo)) {
                    plcReadWriteBo.setToWriteValue("true");
                    try {
                        PlcUtil.write(plcReadWriteBo);
                        // 离站通知写false
                        wcsRequestInTag = locationService.getValueByLocationAndCharact(warehouseLocation.getLocation(), WcsCharactEnum.WCS_HAS_OUT);
                        if (StrUtil.isNotBlank(wcsRequestInTag)) {
                            plcReadWriteBo = plcTagService.getByTagCode(wcsRequestInTag);
                            if (ObjectUtil.isNotNull(plcReadWriteBo)) {
                                plcReadWriteBo.setToWriteValue("false");
                                try {
                                    PlcUtil.write(plcReadWriteBo);
                                } catch (Exception e) {
                                    log.error("脱胶下工装，写离站通知标识失败。");
                                }
                            }
                        }
                    } catch (Exception e) {
                        log.error("脱胶下工装，写请求进站标识失败。");
                    }
                }
            }
        }
    }

    /**
     * 当为扫货架码返回时，需要拿货架号去调MES接口进行下一步动作
     */
    private void reportTaskStatusToMes(HikRcsV4AgvCallback param) {
        String method = param.getCallbackMethod();
        if (StrUtil.equalsIgnoreCase(method, HikCallbackMethodTypeEnum.POD_SCAN.getCode())) {
            // 扫码check
            this.checkPodSn(param);
        } else if (StrUtil.equalsIgnoreCase(method, HikCallbackMethodTypeEnum.OUT_BIN.getCode())) {
            // 任务开始
            this.reportTaskStatusToMes(param, GclMesRunFlagEnum.TASK_START);
        } else if (StrUtil.equalsAnyIgnoreCase(method, HikCallbackMethodTypeEnum.END.getCode(), HikCallbackMethodTypeEnum.FIRST_HALF_END.getCode())) {
            // 任务完成
            this.reportTaskStatusToMes(param, GclMesRunFlagEnum.TASK_FINISH);
        }
    }

    /**
     * 扫码check
     */
    private void checkPodSn(HikRcsV4AgvCallback param) {
        // 调用mes校验接口
        GclMesOperateResult mesOperateResult = this.reportTaskStatusToMes(param, GclMesRunFlagEnum.TASK_CHECK);
        if (ObjectUtil.isNotNull(mesOperateResult) && mesOperateResult.isSuccess()) {
            // mes确认通过，则继续执行RCS任务
            HikRcsOperateResult hikRcsOperateResult = this.taskContinue(param.getCurrentTaskCode(), null);
            if (ObjectUtil.isNull(hikRcsOperateResult) || !hikRcsOperateResult.isSuccess()) {
                // 海康继续任务执行失败，发给mes异常
                this.reportTaskStatusToMes(param, GclMesRunFlagEnum.TASK_WARN);
            }
        } else {
            // mes扫码确认失败后，直接取消任务
            WcsOrderHeader orderHeader = orderHeaderService.getByOrderNoAndOrderType(param.getCurrentTaskCode(), OrderTypeEnums.UP_DOWN.getCode());
            this.taskCancel(orderHeader.getOrderNo());
            orderHeader.setOrderStatus(OrderStatusEnums.CANCEL.getCode());
            orderHeaderService.updateById(orderHeader);
        }
    }

    private GclMesOperateResult reportTaskStatusToMes(HikRcsV4AgvCallback param, GclMesRunFlagEnum runFlagEnum) {
        WcsOrderHeader orderHeader = orderHeaderService.getByOrderNoAndOrderType(param.getCurrentTaskCode(), OrderTypeEnums.UP_DOWN.getCode());
        GclMesTaskStatusReportReq reportReq = new GclMesTaskStatusReportReq();
        reportReq.setId(param.getCurrentTaskCode());
        reportReq.setRunFlag(runFlagEnum.getCode());
        reportReq.setSnCode(StrUtil.isNotBlank(param.getCarrierCode()) ? param.getCarrierCode() : "NA");
        reportReq.setErrorCode(StrUtil.isNotBlank(param.getCarrierCode()) ? StrUtil.EMPTY : GclMesTaskException.CodeEnum.ER14.getCode());
        if (runFlagEnum == GclMesRunFlagEnum.TASK_WARN) {
            reportReq.setErrorCode(GclMesTaskException.CodeEnum.UNKNOWN_ERR.getCode());
        }
        reportReq.setAgvNo(param.getSingleRobotCode());
        reportReq.setMethod(orderHeader.getTaskType());
        return SpringUtils.getBean(IGclMesService.class).taskStatusReportToMes(reportReq);
    }

    /**
     * 处理小车偏差逻辑
     */
    private void handleContainerPosition(HikRcsV4AgvCallback param) {
        if (StrUtil.equalsIgnoreCase(param.getCallbackMethod(), HikCallbackMethodTypeEnum.APPLY_FROM_AGV.getCode())) {
            // 查询出该单据的终点位置
            String endLocation = orderDetailService.getEndLocationByOrderNoAndOrderType(param.getCurrentTaskCode(), OrderTypeEnums.BUSINESS.getCode());
            locationExtendService.setWcsLocationContainerPositionAdjustStatus(endLocation, GclCcsConstants.NG);

            // 延迟N秒，等水车稳定后，再获取值进行计算
            String waitTime = configService.selectConfigByKey("position.wait.time");
            log.info("[二次定位][{}][{}]，收到RCS到位信号，{}秒后获取位置进行计算调整值", endLocation, param.getCurrentTaskCode(), waitTime);
            scheduledExecutorService.schedule(() -> {
                // 获取中控中容器位置和状态
                ContainerPositionBo containerPosition = locationExtendService.getCcsLocationContainerPosition(endLocation);
                if (ObjectUtil.isNull(containerPosition) || StrUtil.equalsIgnoreCase(GclCcsConstants.OK, containerPosition.getCcsPositionStatus())) {
                    // 中控未推送，或者中控判定正常了，则直接结束
                    locationExtendService.setWcsLocationContainerPositionAdjustStatus(endLocation, GclCcsConstants.OK);
                    this.adjustPosition(param.getCurrentTaskCode(), param.getEqptCode(), 0, 0, 0);
                    log.info("[二次定位][{}][{}]，中控判定正常，任务结束：{}", endLocation, param.getCurrentTaskCode(), JSONUtil.toJsonStr(containerPosition));
                    return;
                }

                // 中控判定未到位
                while (!orderHeaderService.taskIsFinished(param.getCurrentTaskCode())) {
                    // 计算出需要调整的值
                    GclCcsLocationPosFixResult posFixResult = gclCcsService.calculatePositionToBeAdjusted(endLocation, param.getCurrentTaskCode());
                    // 如果调整角度计算不正常，则继续重新计算
                    if (!posFixResult.getIsValid()) {
                        log.warn("[二次定位][{}][{}]，中控推送数据不正常，等待2秒后重新获取：{}", endLocation, param.getCurrentTaskCode(), JSONUtil.toJsonStr(posFixResult));
                        Threads.sleep(2000);
                    } else {
                        log.warn("[二次定位][{}][{}]，中控推送数据正常，开始执行RCS位置调整接口：{}", endLocation, param.getCurrentTaskCode(), JSONUtil.toJsonStr(posFixResult));
                        this.adjustPosition(param.getCurrentTaskCode(), param.getEqptCode(),
                            posFixResult.getXFix(), posFixResult.getYFix(), posFixResult.getAngle());
                        break;
                    }
                }
            }, Integer.parseInt(waitTime), TimeUnit.SECONDS);

        }
    }

    /**
     * 处理立库二段任务终点库位设置逻辑
     */
    private void handleLKSecondTaskFinalLocSet(HikRcsV4AgvCallback param) {
        if (StrUtil.equalsIgnoreCase(param.getCallbackMethod(), HikCallbackMethodTypeEnum.LK_FINAL_LOC_SET.getCode())) {
            // 获取到终点位置
            String endLocation = orderDetailService.getEndLocationByOrderNoAndOrderType(param.getCurrentTaskCode(), OrderTypeEnums.BUSINESS.getCode());
            LocationFullInfoBo fullInfoByLocation = locationService.getFullInfoByLocation(endLocation);
            if (ObjectUtil.isNotNull(fullInfoByLocation) && StrUtil.isNotBlank(fullInfoByLocation.getMapMark())) {
                HikRcsOperateResult hikRcsOperateResult = this.taskContinue(param.getCurrentTaskCode(), fullInfoByLocation.getMapMark());
                if (ObjectUtil.isNull(hikRcsOperateResult) || !hikRcsOperateResult.isSuccess()) {
                    // 海康继续任务执行失败，发给mes异常
                    log.error("处理立库二段任务终点库位设置逻辑异常，orderNo={} finalLocation={}", param.getCurrentTaskCode(), endLocation);
                    this.reportTaskStatusToMes(param, GclMesRunFlagEnum.TASK_WARN);
                }
            }
        }
    }

    /**
     * 告警推送
     */
    @Override
    public HikRcsOperateResult warnCallback(HikWarnReq param) {
        // 海康RCS回调结果
        HikRcsOperateResult result = HikRcsOperateResult.builder()
            .code(HikRcsOperateErrorEnum.SUCCESS.getCode())
            .message(HikRcsOperateErrorEnum.SUCCESS.getMessage())
            .reqCode(param.getReqCode())
            .build();

        // 保存告警信息
        wcsWarnService.insertWarnData(param.getData());
        return result;
    }
}
